package BESA.SystemLevel.transactions.gca;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import sample.test.cc.cool.gui.controllers.GCAController;
import BESA.SystemLevel.transactions.gcc.GCC;
import BESA.util.Pair;
import BESA.util.TransactionState;
import BESA.util.VisualSetUp;

public class GCA extends UnicastRemoteObject implements ClientGCA, GCCGCA {

	private static final long serialVersionUID = -1746011914265557791L;
	private long transactionNumbers;
	private long logicalOrder;
	private Map<Long, Set<String>> resources;
	private List<Pair<Long, Long>> stamps;
	

	public GCA() throws RemoteException {
		super();
		transactionNumbers = 0;
		logicalOrder = 0;
		resources = new HashMap<Long, Set<String>>();
		stamps = new ArrayList<Pair<Long, Long>>();
	}

	@Override
	public synchronized boolean closeTransaction(long transID)
			throws RemoteException {
		System.out.println("CLOSE TRANSACTION :" + transID);

		if (!resources.containsKey(transID)) {
			System.out.println("LA TRANSACCIÓN NO EXISTE");
			return false;
		}

		Pair<Long, Long> newOne = new Pair<Long, Long>(transID, logicalOrder++);
		stamps.add(newOne);
		if (VisualSetUp.VISUAL) {
			GCAController.getInstance().updateStatusTrasaction(transID, TransactionState.Validating.toString());
		}
		return this.poll(newOne);
	}

	public Map<Long, Set<String>> getResources() {
		return resources;
	}

	@Override
	public synchronized long openTransaction() throws RemoteException {
		long id = transactionNumbers++;
		System.out.println("OPEN TRANSACTION :" + (id));
		Set<String> resourcesPerTransaction = new HashSet<String>();
		resources.put(id, resourcesPerTransaction);

		if (VisualSetUp.VISUAL) {
			GCAController.getInstance().addTransaction(id);
			GCAController.getInstance().updateStatusTrasaction(id, TransactionState.Working.toString());
		}

		return id;
	}

	@Override
	public synchronized void join(String GCCLocation, String containnerName,long transactionID)
			throws RemoteException {
		// Check if we do a join for a invalid transaction
		if (!resources.containsKey(transactionID)) {
			System.out.println("JOIN IN TID:" + transactionID
					+ ". THE TRANSACTION DOES NOT EXIST");
			return;
		}
		Set<String> resourcesPerTransaction = resources.get(transactionID);
		resourcesPerTransaction.add(GCCLocation);
		System.out.println("JOIN IN TID:" + transactionID
				+ " OF PARTICIPANT:: " + resources.get(transactionID));
		if(VisualSetUp.VISUAL){
			GCAController.getInstance().addParticipant(transactionID, containnerName, GCCLocation);
		}
			
	}

	private synchronized boolean poll(Pair<Long, Long> stamp) {
		boolean ok = true;
		System.out
				.println("--GETTING POLL FOR: TransactionID:"
						+ stamp.getFirst() + "   ValidationNumber:"
						+ stamp.getSecond());

		Set<String> participants = resources.get(stamp.getFirst());
		// Set<String> commiters = new HashSet<String>();

		for (Iterator<String> iterator = participants.iterator(); iterator
				.hasNext();) {
			String actualGCC = iterator.next();
			System.out.println("GCC---------------:" + actualGCC);
			GCC actual = this.getGCC(actualGCC);
			try {
				boolean voteActual = actual.getVote(stamp.getFirst(), stamp
						.getSecond());
				System.out.println("VOTE:" + voteActual);
				if (actual != null && voteActual) {
					// commiters.add(actualGCC);
				} else {
					ok = false;
				}
				if(VisualSetUp.VISUAL){
					GCAController.getInstance().upateVote(stamp.getFirst(), actualGCC, voteActual?"YES":"NO");
				}
			} catch (RemoteException e) {
				e.printStackTrace();
				ok = false;
			}
			System.out.println("-------------------");
		}

		// If there's a problem during the poll
		if (!ok) {
			for (Iterator<String> iterator = participants.iterator(); iterator
					.hasNext();) {
				String toAbort = iterator.next();
				GCC actual = this.getGCC(toAbort);
				try {
					actual.abort(stamp.getFirst());
				} catch (RemoteException e) {
					e.printStackTrace();
					ok = false;
				}
			}
		} else {
			// for (Iterator<String> iterator = commiters.iterator();
			// iterator.hasNext();) {
			for (Iterator<String> iterator = participants.iterator(); iterator
					.hasNext();) {
				String toCommit = iterator.next();
				GCC actual = this.getGCC(toCommit);
				try {
					actual.commit(stamp.getFirst(), stamp.getSecond());
				} catch (RemoteException e) {
					e.printStackTrace();
					ok = false;
				}
			}
		}

		System.out.println("Final Vote Poll:" + ok);
		System.out.println("-------------------");
		if (VisualSetUp.VISUAL) {

			if (ok)
				GCAController.getInstance().updateStatusTrasaction(stamp.getFirst(), TransactionState.Commited.toString());
			else
				GCAController.getInstance().updateStatusTrasaction(stamp.getFirst(), TransactionState.Aborted.toString());
		}
		return ok;
	}

	private GCC getGCC(String GCCLocation) {
		System.out.println("LOOKING FOR " + GCCLocation);
		GCC ret = null;
		try {
			ret = (GCC) Naming.lookup(GCCLocation);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		}
		return ret;
	}


}
